/******************************************************************************* * Copyright (c) 2000, 2008 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.internal.ui.refactoring; import java.lang.reflect.InvocationTargetException; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.viewers.IColorProvider; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.LabelProviderChangedEvent; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.wizard.IWizardPage; import org.eclipse.ui.PlatformUI; import org.eclipse.ltk.ui.refactoring.RefactoringWizard; import org.eclipse.ltk.ui.refactoring.UserInputWizardPage; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.internal.corext.refactoring.structure.ChangeTypeRefactoring; import org.eclipse.jdt.internal.corext.util.Messages; import org.eclipse.jdt.internal.ui.IJavaHelpContextIds; import org.eclipse.jdt.internal.ui.JavaPlugin; import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels; import org.eclipse.jdt.internal.ui.viewsupport.BindingLabelProvider; /** * @author tip */ public class ChangeTypeWizard extends RefactoringWizard { private ChangeTypeRefactoring fCT; public ChangeTypeWizard(ChangeTypeRefactoring ref) { super(ref, DIALOG_BASED_USER_INTERFACE); setDefaultPageTitle(RefactoringMessages.ChangeTypeWizard_title); fCT= ref; } /* non java-doc * @see RefactoringWizard#addUserInputPages */ protected void addUserInputPages(){ addPage(new ChangeTypeInputPage()); } // For debugging static String print(Collection/*<ITypeBinding>*/ types){ if (types.isEmpty()) return "{ }"; //$NON-NLS-1$ String result = "{ "; //$NON-NLS-1$ for (Iterator it=types.iterator(); it.hasNext(); ){ ITypeBinding type= (ITypeBinding)it.next(); result += type.getQualifiedName(); if (it.hasNext()){ result += ", "; //$NON-NLS-1$ } else { result += " }"; //$NON-NLS-1$ } } return result; } /** * A JavaElementLabelProvider that supports graying out of invalid types. */ private class ChangeTypeLabelProvider extends BindingLabelProvider implements IColorProvider { private Color fGrayColor; private HashMap/*<Image color, Image gray>*/ fGrayImages; public ChangeTypeLabelProvider(){ fGrayColor= Display.getCurrent().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW); fGrayImages= new HashMap(); } private Collection/*<ITypeBinding>*/ fInvalidTypes; public void grayOut(Collection/*<ITypeBinding>*/ invalidTypes){ fInvalidTypes= invalidTypes; /* * Invalidate all labels. Invalidating only invalid types doesn't * work since there can be multiple nodes in the tree that * correspond to the same invalid IType. The TreeViewer only updates * the label of one of these ITypes and leaves the others in their * old state. */ fireLabelProviderChanged(new LabelProviderChangedEvent(this)); } /* (non-Javadoc) * @see org.eclipse.jface.viewers.IColorProvider#getForeground(java.lang.Object) */ public Color getForeground(Object element) { if (isInvalid(element)) return fGrayColor; else return null; } private boolean isInvalid(Object element) { if (fInvalidTypes == null) return false; // initially, everything is enabled else return fInvalidTypes.contains(element); } /* (non-Javadoc) * @see org.eclipse.jface.viewers.IColorProvider#getBackground(java.lang.Object) */ public Color getBackground(Object element) { return null; } /* * @see org.eclipse.jface.viewers.ILabelProvider#getImage(java.lang.Object) */ public Image getImage(Object element) { Image image= super.getImage(element); if (isInvalid(element) && image != null) { Image grayImage= (Image) fGrayImages.get(image); if (grayImage == null) { grayImage= new Image(Display.getCurrent(), image, SWT.IMAGE_GRAY); fGrayImages.put(image, grayImage); } return grayImage; } else { return image; } } public void dispose() { for (Iterator iter= fGrayImages.values().iterator(); iter.hasNext();) { Image image= (Image) iter.next(); image.dispose(); } fGrayImages.clear(); super.dispose(); } } private class ChangeTypeInputPage extends UserInputWizardPage{ public static final String PAGE_NAME= "ChangeTypeInputPage";//$NON-NLS-1$ private final String MESSAGE= RefactoringMessages.ChangeTypeInputPage_Select_Type; private ChangeTypeLabelProvider fLabelProvider; private TreeViewer fTreeViewer; private boolean fTreeUpdated= false; public ChangeTypeInputPage() { super(PAGE_NAME); setMessage(MESSAGE); } private class ValidTypesTask implements Runnable { private Collection/*<ITypeBinding>*/ fInvalidTypes; private Collection/*<ITypeBinding>*/ fValidTypes; public void run() { IRunnableWithProgress runnable= new IRunnableWithProgress() { public void run(IProgressMonitor pm) { pm.beginTask(RefactoringMessages.ChangeTypeWizard_analyzing, 1000); ChangeTypeRefactoring ct= (ChangeTypeRefactoring)ChangeTypeWizard.this.getRefactoring(); fInvalidTypes = new HashSet(); fInvalidTypes.addAll(fCT.getAllSuperTypes(ct.getOriginalType())); fValidTypes= ct.computeValidTypes(new SubProgressMonitor(pm, 950)); fInvalidTypes.add(ct.getOriginalType()); fInvalidTypes.removeAll(fValidTypes); pm.worked(50); pm.done(); } }; boolean internalError= false; try { getWizard().getContainer().run(true, true, runnable); } catch (InvocationTargetException e) { internalError= true; JavaPlugin.log(e); ChangeTypeInputPage.this.setErrorMessage(RefactoringMessages.ChangeTypeWizard_internalError); } catch (InterruptedException e) { ChangeTypeInputPage.this.setMessage(RefactoringMessages.ChangeTypeWizard_computationInterrupted); } fLabelProvider.grayOut(fInvalidTypes); if (internalError) { setPageComplete(false); } else if (fValidTypes == null || fValidTypes.size() == 0){ ChangeTypeInputPage.this.setErrorMessage(RefactoringMessages.ChangeTypeWizard_declCannotBeChanged); setPageComplete(false); } else { TreeItem selection= getInitialSelection(fValidTypes); fTreeViewer.getTree().setSelection(new TreeItem[]{ selection }); setPageComplete(true); ChangeTypeInputPage.this.setMessage(""); //$NON-NLS-1$ } } } private TreeItem getInitialSelection(Collection/*<ITypeBinding>*/ types) { // first, find a most general valid type (there may be more than one) ITypeBinding type= (ITypeBinding)types.iterator().next(); for (Iterator it= types.iterator(); it.hasNext(); ){ ITypeBinding other= (ITypeBinding)it.next(); if (getGeneralizeTypeRefactoring().isSubTypeOf(type, other)){ type= other; } } // now find a corresponding TreeItem (there may be more than one) return findItem(fTreeViewer.getTree().getItems(), type); } private TreeItem findItem(TreeItem[] items, ITypeBinding type){ for (int i=0; i < items.length; i++){ if (items[i].getData().equals(type)) return items[i]; } for (int i=0; i < items.length; i++){ TreeItem item= findItem(items[i].getItems(), type); if (item != null) return item; } return null; } public void createControl(Composite parent) { Composite composite= new Composite(parent, SWT.NONE); setControl(composite); composite.setLayout(new GridLayout()); composite.setLayoutData(new GridData()); Label label= new Label(composite, SWT.NONE); label.setText(Messages.format( RefactoringMessages.ChangeTypeWizard_pleaseChooseType, ((ChangeTypeRefactoring) getRefactoring()).getTarget())); label.setLayoutData(new GridData()); addTreeComponent(composite); Dialog.applyDialogFont(composite); PlatformUI.getWorkbench().getHelpSystem().setHelp(getControl(), IJavaHelpContextIds.CHANGE_TYPE_WIZARD_PAGE); } /** * Tree-viewer that shows the allowable types in a tree view. * @param parent the parent */ private void addTreeComponent(Composite parent) { fTreeViewer= new TreeViewer(parent, SWT.SINGLE | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL); GridData gd= new GridData(GridData.FILL_BOTH); gd.grabExcessHorizontalSpace= true; gd.grabExcessVerticalSpace= true; GC gc= null; try { gc= new GC(parent); gc.setFont(gc.getFont()); gd.heightHint= Dialog.convertHeightInCharsToPixels(gc.getFontMetrics(), 6); // 6 characters tall } finally { if (gc != null) { gc.dispose(); gc= null; } } fTreeViewer.getTree().setLayoutData(gd); fTreeViewer.setContentProvider(new ChangeTypeContentProvider(((ChangeTypeRefactoring)getRefactoring()))); fLabelProvider= new ChangeTypeLabelProvider(); fTreeViewer.setLabelProvider(fLabelProvider); ISelectionChangedListener listener= new ISelectionChangedListener(){ public void selectionChanged(SelectionChangedEvent event) { IStructuredSelection selection= (IStructuredSelection)event.getSelection(); typeSelected((ITypeBinding)selection.getFirstElement()); } }; fTreeViewer.addSelectionChangedListener(listener); fTreeViewer.setInput(new ChangeTypeContentProvider.RootType(getGeneralizeTypeRefactoring().getOriginalType())); fTreeViewer.expandToLevel(10); } private void typeSelected(ITypeBinding type) { boolean isValid= getGeneralizeTypeRefactoring().getValidTypes().contains(type); ChangeTypeInputPage.this.setPageComplete(isValid); if (isValid) { ChangeTypeInputPage.this.setMessage(""); //$NON-NLS-1$ } else { if (getGeneralizeTypeRefactoring().getOriginalType().equals(type)) { ChangeTypeInputPage.this.setMessage(Messages.format( RefactoringMessages.ChangeTypeWizard_with_itself, BasicElementLabels.getJavaElementName(type.getName()))); } else { ChangeTypeInputPage.this.setMessage(Messages.format( RefactoringMessages.ChangeTypeWizard_grayed_types, new Object[] {BasicElementLabels.getJavaElementName(type.getName()), BasicElementLabels.getJavaElementName(getGeneralizeTypeRefactoring().getOriginalType().getName())})); } } } private ChangeTypeRefactoring getGeneralizeTypeRefactoring(){ return (ChangeTypeRefactoring)getRefactoring(); } /* * @see org.eclipse.jface.wizard.IWizardPage#getNextPage() */ public IWizardPage getNextPage() { initializeRefactoring(); return super.getNextPage(); } private ITypeBinding getSelectedType() { IStructuredSelection ss= (IStructuredSelection)fTreeViewer.getSelection(); return (ITypeBinding)ss.getFirstElement(); } /* * @see org.eclipse.jdt.internal.ui.refactoring.RefactoringWizardPage#performFinish() */ public boolean performFinish(){ initializeRefactoring(); return super.performFinish(); } private void initializeRefactoring() { getGeneralizeTypeRefactoring().setSelectedType(getSelectedType()); } /* * @see org.eclipse.jface.dialogs.IDialogPage#dispose() */ public void dispose() { fTreeViewer= null; super.dispose(); } /* (non-Javadoc) * @see org.eclipse.jface.dialogs.IDialogPage#setVisible(boolean) */ public void setVisible(boolean visible) { super.setVisible(visible); if (visible && fTreeViewer != null) fTreeViewer.getTree().setFocus(); if (!fTreeUpdated){ fTreeViewer.getTree().getDisplay().asyncExec(new ValidTypesTask()); fTreeUpdated= true; } } } }